`include "common.svh"

module csr #(
    parameter COMMIT_WIDTH = 2
) (
    input           clk,
    input           rst,
    //Read/Write Port
    input  CSR_ADDR csr_addr,
    input           csr_read,
    input           csr_write,
    input           csr_set,
    input           csr_clear,
    input  word_t   csr_wdata,
    output word_t   csr_rdata,
    //CSR Data
    output word_t   mepc,
    output word_t   mtvec,
    //CSR Ctrl Port
    // input            exeception_valid,
    // input  EXPT_CODE exeception_code,


    input commit_valid[COMMIT_WIDTH-1:0]

);
  localparam MSTATUS_ADDR = 12'h300;
  localparam MTVEC_ADDR = 12'h305;
  localparam MEPC_ADDR = 12'h341;
  localparam MCAUSE_ADDR = 12'h342;
  localparam MCYCLE_ADDR = 12'hB00;
  localparam MINSTRET_ADDR = 12'hB02;
  word_t mcycle, minstret, mstatus, mcause;

  //addr decode
  wire sel_mstatus = csr_addr == MSTATUS_ADDR;
  wire sel_mtvec = csr_addr == MTVEC_ADDR;
  wire sel_mepc = csr_addr == MEPC_ADDR;
  wire sel_mcause = csr_addr == MCAUSE_ADDR;
  wire sel_mcycle = csr_addr == MCYCLE_ADDR;
  wire sel_minstret = csr_addr == MINSTRET_ADDR;

  word_t csr_data;
  assign csr_rdata = csr_read ? csr_data : 'b0;
  //CSR Read Port
  MUX_OH #(6, `WORD_BITS) mux_csr_data (
      .sel ({sel_mstatus, sel_mtvec, sel_mepc, sel_mcause, sel_mcycle, sel_minstret}),
      .din ({mstatus, mtvec, mepc, mcause, mcycle, minstret}),
      .dout(csr_data)
  );
  //CSR Write Port
  word_t csr_write_data, csr_set_data, csr_clear_data;
  assign csr_write_data = csr_wdata;
  assign csr_set_data   = csr_data | csr_wdata;
  assign csr_clear_data = csr_data & (~csr_wdata);
  word_t csr_next_data;
  wire csr_next_data_valid = csr_write | csr_set | csr_clear;
  MUX_OH #(3, `WORD_BITS) mux_csr_next_data (
      .sel ({csr_write, csr_set, csr_clear}),
      .din ({csr_write_data, csr_set_data, csr_clear_data}),
      .dout(csr_next_data)
  );

  //mcycle
  word_t mcycle_next;
  assign mcycle_next = (csr_next_data_valid & sel_mcycle) ? csr_next_data : mcycle + 'b1;
  reg_r #(`WORD_BITS) mcycle_r (
      .din (mcycle_next),
      .dout(mcycle),
      .*
  );
  //minstret
  word_t minstret_next;
  always_comb begin
    integer i;
    minstret_next = minstret;
    for (i = 0; i < COMMIT_WIDTH; i = i + 1) begin
      if (commit_valid[i]) minstret_next = minstret_next + 'b1;
    end
    if (csr_next_data_valid & sel_minstret) minstret_next = csr_next_data;
  end
  reg_r #(`WORD_BITS) minstret_r (
      .din (minstret_next),
      .dout(minstret),
      .*
  );

  assign mepc = 'b0;
  assign mtvec = 'b0;
  assign mstatus = 'b0;
  assign mcause = 'b0;
endmodule
